package com.guo.anim.particle;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatImageView;

import com.guo.anim.R;
import com.guo.anim.fallingstar.RoundBitmapUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 通过重写ImageView实现粒子环绕图形图片的效果
 */
public class ParticleView extends AppCompatImageView {
    private static final String TAG = "ParticleView";
    //视图的宽度和高度
    private int width , height;
    //中心圆形图片的半径，规则为直径为最小边长的1/2
    private int mInsideImageRadius;
    //粒子活动范围最大外径
    private int mOutSideStarRadius;
    //专辑图外大圆的半径
    private int mAlbumCircleRadius;
    //中心图片的旋转Matrix
    private Matrix mMatrix = new Matrix();
    //中心图片的旋转角度
    private int mRotateAngle = 0;
    private Random mRandomInt = new Random();
    //粒子集合
    private List<Particle> mParticleList = new ArrayList<>();
    //气泡集合
    private List<Particle> mBubbleList = new ArrayList<>();
    //分别为专辑图外圆画笔，粒子中的气泡话题，粒子画笔
    private Paint mCirclePaint, mBubblePaint, mParticlePaint;

    //圆形样式的Bitmap
    Bitmap roundBitmap;


    //粒子最大组数
    private int PARTICLE_GROUP_SIZE = 100;
    //每个粒子组个数
    private int PARTICLE_SIZE = 1;

    public ParticleView(Context context) {
        super(context);
        initPaint();
    }

    public ParticleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public ParticleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    private void initPaint(){
        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(false);
        mCirclePaint.setStyle(Paint.Style.STROKE);
        mCirclePaint.setStrokeWidth(3);
        mCirclePaint.setColor(getResources().getColor(R.color.gray_transparent));
        mParticlePaint = new Paint();
        mParticlePaint.setAntiAlias(false);
        mParticlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mParticlePaint.setColor(getResources().getColor(R.color.white));
        mBubblePaint = new Paint();
        mBubblePaint.setAntiAlias(false);
        mBubblePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mBubblePaint.setAlpha(80);
        setBackgroundColor(0x00E0E0E0);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
        //计算流星最外轨道半径
        mOutSideStarRadius = Math.min(width, height) / 2 * 9 / 10;
        //计算中心原图的半径
        mInsideImageRadius = mOutSideStarRadius * 2 / 3;
        //计算适合的粒子群个数
        mAlbumCircleRadius = (mInsideImageRadius + 20) < mOutSideStarRadius ? (mInsideImageRadius + 20) : (mInsideImageRadius + (int)mCirclePaint.getStrokeWidth()/2);
        //平均间隔15个像素的圆环分配一个粒子组
        PARTICLE_GROUP_SIZE = (int)(mAlbumCircleRadius * 2 *Math.PI / 15);
        //初始化粒子群
        initParticleGroup(PARTICLE_GROUP_SIZE);
        //初始化气泡群
        initBubbleGroup(PARTICLE_GROUP_SIZE / 100);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //设置画布透明
        canvas.drawARGB(0,0,0,0);
        //绘制中间的圆形图片
        Drawable drawable = getDrawable();
        if(null == drawable){
            return;
        }
        //避免重复生产圆形图片和提取主题色
        if(null == roundBitmap){
            //将ImageView的原图裁剪成圆形图片
            Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
            roundBitmap = RoundBitmapUtil.createRoundBitmap(bitmap, mInsideImageRadius);
            //提取主题色
            int colorTheme = BitmapColorUtil.extractColor(bitmap);
            mCirclePaint.setColor(colorTheme);
            mParticlePaint.setColor(colorTheme);
            mBubblePaint.setColor(colorTheme);
        }

        //通过Matrix设置圆形Bitmap旋转
        mMatrix.reset();
        mMatrix.setRotate(mRotateAngle);
        //获取旋转后的Bitmap
        Bitmap rotateBitmap = Bitmap.createBitmap(roundBitmap, 0, 0, 2*mInsideImageRadius, 2*mInsideImageRadius, mMatrix, false);
        //在画布上绘制旋转后的Bitmap，注意基于Matrix旋转后的Bitmap与原图的大小并不相等，故计算中心位置时应以转换后的Bitmap进行计算
        canvas.drawBitmap(rotateBitmap, width / 2 - rotateBitmap.getWidth()/2 , height / 2 - rotateBitmap.getHeight()/2, null);
        //绘制专辑图外围的圆环
        canvas.drawCircle(width/2, height/2, mAlbumCircleRadius, mCirclePaint);
        //绘制粒子群
        drawParticleGroup(canvas);
        //绘制气泡群
        drawBubbleGroup(canvas);
        //100ms后更新粒子位置和气泡位置
        postDelayed(new Runnable() {
            @Override
            public void run() {
                changeParticleGroup();
                changeBubbleGroup();
                //补充粒子
                initParticleGroup(PARTICLE_GROUP_SIZE);
                //补充气泡
                initBubbleGroup(PARTICLE_GROUP_SIZE / 60);
            }
        }, 100);
    }

    /**
     * 减少bitmap反复转换的消耗
     * @param bm
     */
    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        //将ImageView的原图裁剪成圆形图片
        roundBitmap = null;
        //提取主题色
        int colorTheme = BitmapColorUtil.extractColor(bm);
        mCirclePaint.setColor(colorTheme);
        mParticlePaint.setColor(colorTheme);
        mBubblePaint.setColor(colorTheme);
    }

    //添加粒子群
    private void initParticleGroup(int size){
        //每一个起始的粒子占据的角度范围
        float angleInterval = 1.0f * 360 / size;
        //粒子所处角度
        float angle;
        for(int i = 0; i < size; i++){
            for(int j = 0; j < PARTICLE_SIZE; j++){
                angle = (i * angleInterval + mRandomInt.nextInt(10)*angleInterval / 10)*1.0f;
                int x = (int)(Math.cos(2 * Math.PI * angle / 360) * mAlbumCircleRadius) + width/2;
                int y = (int)(Math.sin(2 * Math.PI * angle / 360) *mAlbumCircleRadius) +height/2;
                Particle particle = new Particle().setX(x).setY(y).build();
                mParticleList.add(particle);
            }
        }
    }

    /**
     * 绘制粒子群
     * @param canvas 画布
     */
    private void drawParticleGroup(Canvas canvas){
        for(int i = 0; i < mParticleList.size(); i++){
            Particle particle = mParticleList.get(i);
            mParticlePaint.setAlpha(particle.getAlpha());
            canvas.drawCircle(particle.getX(), particle.getY(), particle.getR(), mParticlePaint);
        }
    }

    /**
     * 改变粒子群信息
     */
    private void changeParticleGroup(){
        for(int i = 0; i < mParticleList.size(); i++){
            mParticleList.get(i).change();
        }
        //调整中心图片的旋转角度，设置进行逆时针旋转
        mRotateAngle -= 2;
        mRotateAngle = (mRotateAngle + 360) % 360;
        invalidate();
    }

    /**
     * 添加气泡群
     */
    private void initBubbleGroup(int size){
        //每一个起始的气泡占据的角度范围
        float angleInterval = 1.0f * 360 / size;
        //气泡所处角度
        float angle;
        for(int i = 0; i < size; i++){
            angle = (i * angleInterval + mRandomInt.nextInt(10)*angleInterval / 10)*1.0f;
            int x = (int)(Math.cos(2 * Math.PI * angle / 360) * mAlbumCircleRadius) + width/2;
            int y = (int)(Math.sin(2 * Math.PI * angle / 360) *mAlbumCircleRadius) +height/2;
            Particle particle = new Bubble().setX(x).setY(y).build();
            mBubbleList.add(particle);
        }
    }

    /**
     * 改变气泡群信息
     */
    private void changeBubbleGroup(){
        for(int i = 0; i < mBubbleList.size(); i++){
            mBubbleList.get(i).change();
        }
    }

    private void drawBubbleGroup(Canvas canvas){
        for(int i = 0; i < mBubbleList.size(); i++){
            Particle particle = mBubbleList.get(i);
            mBubblePaint.setAlpha(particle.getAlpha());
            canvas.drawCircle(particle.getX(), particle.getY(), particle.getR(), mBubblePaint);
        }
    }

    /**
     * 粒子信息由半径，x坐标，y坐标，alpha透明度，velocity移动速度构成
     * 半径范围控制在1~2以内
     * alpha呈现减少趋势
     */
    private class Particle implements CircleFactory<Particle>{
        private int r;
        private int x;
        private int y;
        private int alpha;
        private int velocity;
        //斜率
        private float slope;
        //方向,x向增加为1，x向减小为-1
        private int direction;
        //粒子寿命,velocity降为0或者超出粒子云外径视为寿命终结
        private int age;
        private float sqrt;

        @Override
        public Particle setX(int x) {
            this.x = x;
            return this;
        }

        @Override
        public Particle setY(int y) {
            this.y = y;
            return this;
        }

        public int getR() {
            return r;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public int getAlpha() {
            return alpha;
        }

        public float getSlope() {
            return slope;
        }

        public int getDirection() {
            return direction;
        }

        public int getVelocity() {
            return velocity;
        }

        public int getAge() {
            return age;
        }

        public void setR(int r) {
            this.r = r;
        }

        public void setAlpha(int alpha) {
            this.alpha = alpha;
        }

        public void setVelocity(int velocity) {
            this.velocity = velocity;
        }

        public void setSlope(float slope) {
            this.slope = slope;
        }

        public void setDirection(int direction) {
            this.direction = direction;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public void setSqrt(float sqrt) {
            this.sqrt = sqrt;
        }

        public float getSqrt() {
            return sqrt;
        }

        @Override
        public Particle build() {
            r = mRandomInt.nextInt(2) + 1;
            alpha = 255;
            velocity = mRandomInt.nextInt(3) + 3;
            slope = 1.0f*(y - height/2) / (x - width/2);
            direction = (x >= width/2 ? 1 : -1);
            //计算age值
            int moveAge = (int)Math.sqrt(Math.pow(mOutSideStarRadius, 2)*1.0f - (Math.pow(x - width/2, 2) + Math.pow(y - height/2, 2))*1.0f) / velocity + 1;
            int alphaAge = alpha / 30 + 1;
            age = Math.min(moveAge, alphaAge);
            sqrt = (float) (1.0f / Math.sqrt(1 + Math.pow(slope, 2)));
            return this;
        }

        /**
         * 改变该粒子的x,y坐标
         */
        @Override
        public void change() {
            x += direction*velocity *sqrt;
            y += direction*velocity*slope *sqrt;
            alpha -=30;
            ageOver();
        }

        /**
         * 粒子寿命终结时移除
         */
        public void ageEnd(){
            boolean exceedingBoarder = (Math.pow(x - width/2, 2) + Math.pow(y - height/2, 2) - Math.pow(mOutSideStarRadius, 2) >= 0.1f);
            boolean moveState = (velocity > 0);
            boolean visible = (alpha > 0);
            if(exceedingBoarder | !moveState | !visible){
                remove();
            }
        }

        public void ageOver(){
            age--;
            boolean moveState = (velocity > 0);
            if(age <= 0 | !moveState){
                remove();
            }
        }

        @Override
        public void remove(){
            mParticleList.remove(this);
        }
    }

    /**
     * 气泡的整体元素复用粒子，差异点在于透明度偏低，半径较大，调整速度较慢
     */
    private class Bubble extends Particle{

        @Override
        public Bubble build() {
            setR(mRandomInt.nextInt(10) + 5);
            setAlpha(100);
            setVelocity(mRandomInt.nextInt(2) + 2);
            setSlope(1.0f*(getY() - height/2) / (getX() - width/2));
            setDirection(getX() >= width/2 ? 1 : -1);
            //计算age值
            int moveAge = ((int)Math.sqrt(Math.pow(mOutSideStarRadius, 2) - (Math.pow(getY() - width/2, 2) + Math.pow(getY() - height/2, 2))) + 100) / getVelocity() + 1;
            int alphaAge = getAlpha() / 10 + 1;
            setAge(Math.min(moveAge, alphaAge));
            setSqrt((float) (1.0f / Math.sqrt(1 + Math.pow(getSlope(), 2)))*1.0f);
            return this;
        }

        @Override
        public void change() {
            setX((int)(getX() + getDirection() * getVelocity() * getSqrt()));
            setY((int)(getY() + getDirection() * getVelocity() * getSlope() * getSqrt()));
            setAlpha(getAlpha() - 10);
            ageOver();
        }

        @Override
        public void remove() {
            mBubbleList.remove(this);
        }
    }
}
